import io
import logging
from django.contrib.auth.models import User
from django.http import HttpResponse
from django_redis import get_redis_connection
from django.shortcuts import render
from django.views import View
from rest_framework.permissions import IsAuthenticated
from utils.customPermissions import ActiveUserPermission, auto_user
from utils.verifyUtil import ImageVerify
from user.serializers import *
from rest_framework_simplejwt.views import TokenObtainSlidingView
from rest_framework.viewsets import ModelViewSet, GenericViewSet, ReadOnlyModelViewSet
from rest_framework.mixins import *
from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from rest_framework.decorators import action
from rest_framework_extensions.cache.mixins import CacheResponseMixin
from fdfs_client.client import Fdfs_client, get_tracker_conf
from config.dbs.redisconfig import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from config.fastdfs.config import FASTDFS_SERVER_DOMAIN
from user.models import UserDetail, Area

logger = logging.getLogger(__name__)  # 以当前文件名生成日志
client_conf = get_tracker_conf('config/fastdfs/client.conf')  # 配置client.conf
client = Fdfs_client(client_conf)  # 生成一个客户端（tracker配置）


class MyTokenView(TokenObtainSlidingView):
    serializer_class = MyTokenSerializer


class ImageVerifyView(View):  # 不需要mysql，所以不用继承ModelViewSet
    def get(self, request, uuid):
        """
        获取图形验证码
        :param request:
        :return:
        """
        imgVerify = ImageVerify()
        img, code = imgVerify.image_code()

        # 图片以二进制形式传输
        # 创建一个字节流管道
        bytes_box = io.BytesIO()
        # 把图片以png格式保存到字节流管道
        img.save(bytes_box, format='PNG')
        # 从字节流管道获取二进制图片
        image_bytes = bytes_box.getvalue()

        # code保存到redis数据库
        cache = get_redis_connection(alias="verify_codes")  # alias指定保存到哪个数据库，如果不写则保存到默认数据库
        cache.set(LOGIN_KEY_TEMPLATE % uuid, code, EXPIRE_TIME)  # redis数据库保存的形式（key:value）

        return HttpResponse(image_bytes, content_type='image/png')


class UserViewSet(RetrieveModelMixin, GenericViewSet):
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer
    permission_classes = [ActiveUserPermission]

    # 重写get_serializer，当接口是'password'，则调用UpdatePasswordSerializer序列化器
    def get_serializer(self, *args, **kwargs):
        if self.action == 'password':
            return UpdatePasswordSerializer(*args, **kwargs)
        return self.serializer_class(*args, **kwargs)

    # 因为涉及到User表和UserDetail表2个表的修改，所以重写update方法进行自定义修改用户信息，而不再继承UpdateModelMixin
    def update(self, request, pk):
        # 1.User表的更新
        # -通过pk查询到User对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # -拿到前端传入的数据request.data，反序列化
        user_serializer = self.get_serializer(user, data=request.data)  # 调用上面自己的serializer_class
        # -字段校验is_valid
        user_serializer.is_valid(raise_exception=True)
        # -更新保存
        user_serializer.save()

        # 2.UserDetail表额更新
        # -判断用户有没详情对象，获取前端传入的数据request.data['userdetail']
        user_detail = request.data['userdetail']
        if hasattr(user, 'userdetail'):
            # -有，拿到前端传入的数据进行更新；没有，创建数据。反序列化
            user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail)  # user.usetdetail反向查询
        else:
            user_detail_serializer = UserDetailSerializer(data=user_detail)
        # -字段校验is_valid
        user_detail_serializer.is_valid(raise_exception=True)
        # -更新保存
        user_detail_serializer.save()

        # 构造返回的数据结构
        user_serializer.data['userdetail'] = user_detail_serializer.data
        return Response(user_serializer.data)

    @action(methods=['put'], detail=True)
    def password(self, request, pk):
        # 从模型，根据pk拿到用户对象
        try:
            user = User.objects.get(pk=pk)
        except user.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 从前端拿到传入的密码数据request.data，反序列化
        password = request.data['password']
        new_password = request.data['new_password']
        serializer = self.get_serializer(data=request.data)  # 调用自己的serializer_class，但get_serializer要重写
        # 字段校验
        serializer.is_valid(raise_exception=True)
        # 判断提交的旧密码和数据库里存放的是否一致
        if user.check_password(password):
            # 一致，则修改成功
            user.set_password(new_password)
            user.save()
            return Response({'msg': '密码修改成功'})
        # 错误，则返回状态码，终止程序
        return Response(status=HTTP_400_BAD_REQUEST)

    @action(methods=['post'], detail=True)
    def avatar(self, request, pk):
        # 根据pk获取用户对象
        try:
            user = User.objects.get(pk=pk)
        except user.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 获取传入的信息（头像图片文件：request.FILES）
        files = request.FILES.get('file')
        # 校验
        if not files or files.content_type not in ('image/jpeg', 'image/png', 'image/gif', 'image/jpg'):
            return Response(status=HTTP_400_BAD_REQUEST)
        # 上传头像图片到FastDFS，首先拿到图片文件的后缀名
        try:  # eg：1.jpg，以'.'作为分割，[-1]是下标，拿最后一个
            image_ext_name = files.name.split('.')[-1]
        except Exception as e:
            image_ext_name = 'png'
        try:  # 因为传入头像信息是二进制，因此用upload_by_buffer
            res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)
        except Exception as e:
            logger.error(f'图片上传错误：{e}')
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if res.get('Status') != 'Upload successed.':
                return Response({'msg': '图片上传失败，请稍后重试或联系管理员'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                # 将返回的地址储存到mysql数据库（详情表user.userdetail.avatar）
                # 因为res拿到的地址是二进制的，所以需要需要decode()解码
                image_url = FASTDFS_SERVER_DOMAIN + res.get('Remote file_id').decode()
                if hasattr(user, 'userdetail'):  # 判断用户是否有详情
                    # 有则修改
                    user_detail = user.userdetail
                    user_detail.avatar = image_url
                    user_detail.save()
                else:  # 无，则创建
                    UserDetail.objects.create(avatar=image_url, user=user)
                # 返回响应给前端
                return Response({'data': image_url})


# class AreaViewSet(ListModelMixin, GenericViewSet, RetrieveModelMixin) = class AreaViewSet(ReadOnlyModelViewSet)
class AreaViewSet(CacheResponseMixin, ReadOnlyModelViewSet):  # 先从缓存拿数据，没有则从数据库拿数据
    # queryset = Area.objects.filter(parent=None)  # 查询省份，没有上级行政区
    def get_queryset(self):
        if self.action == 'list':
            return Area.objects.filter(parent=None)  # 查询省份，没有上级行政区
        else:
            return Area.objects.all()  # 查询省份下所有城市

    # serializer_class = AreaSerializer

    def get_serializer_class(self):
        if self.action == 'list':
            return AreaSerializer  # 查询省份
        else:
            return ParentSerializer  # 查询省份下所有城市

    permission_classes = [IsAuthenticated]


class AddressViewSet(ModelViewSet):
    # 查询集：只能查询当前登录用户的地址
    def get_queryset(self):
        return self.request.user.address_set.filter(is_delete=False)

    serializer_class = AddressSerializer
    permission_classes = [IsAuthenticated]

    @auto_user
    def create(self, request, *args, **kwargs):
        return CreateModelMixin.create(self, request, *args, **kwargs)

    @auto_user
    def update(self, request, *args, **kwargs):
        return UpdateModelMixin.update(self, request, *args, **kwargs)
